再來我們來介紹,如果要做 web 相關應用服務,免不了一定有 database 相關需求,我們就以 mysql 當做例子,作為今天主題
golang 其實在 database 的存取上,他設計了一個 sql 抽象介面叫做 database/sql,接下來只要有不同人的遵照這個 interface 分開去實作 mysql、sqlite ... 等等,這是一個很棒的設計,如果未來你有測試或是抽換需求,其實只要更新 driver ,但完全不需要更動你的程式相關地方
在這邊簡單介紹一下 golang mysql driver ,最知名的應該是這套 go-sql-driver/mysql
先從連線做起
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
//完整的資料格式連線如下
//[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
db, err := sql.Open("mysql", "user:password@/dbname")
再來如果要執行寫入
result, err := db.Exec(
"INSERT INTO user_info (name, age) VALUES (?, ?)",
"syhlion",
18,
)
執行查詢(單筆)
var age int
row := db.QueryRow("SELECT age FROM user_info WHERE name = ?","syhlion")
err := row.Scan(&age)
執行查詢(多筆)
rows, err := db.Query("SELECT name FROM user_info WHERE age > ?", 16)
if err != nil {
log.Fatal(err)
}
//切記用完都要做 Close
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
如果要 transcation
tx, err := db.Begin()
if err != nil {
return
}
defer func(){
//如果最後有錯誤,則執行 rollback
if err != nil {
tx.RollBack()
}
}
...
...
...
tx.Commit()
在這邊我提供了一個,我自己二度包裝過的套件 syhlion/sqlwrapper,基本上都跟上面所提供的方法或使用方式一模一樣,只是我稍微包裝過有添加了一些 log、監控、debug... 相關功能<
,用法如下
func main(){
db, err := sql.Open("xxx","xxx")
if err != nil {
return nil, err
}
//這邊第二參數為是否開啟 debug 模式,如為 true 則強制開啟 sql log模式
//第三個參數為如果sql 執行速度慢於多少時間,則會印出該筆 sql 的 log
db := WrapperDB(db,true,1*time.Second)
// it log [sql] select * from member where id = ? 1 2s
rs,err:=db.Exec("select * from member where id = ?",1)
if err != nil {
return
}
}
印出的 log 樣子如下
{
"args": [
"syhlion",
18
],
"ip": "172.18.0.12",
"level": "debug",
"msg": "db query",
"name": "syhlion/sqlwrapper",
"sql": "SELECT * FROM user_info WHERE name = ? AND age = ?",
"time": "2018-11-01T16:55:00+08:00",
"use-time": "5.7768ms"
}
會有這樣的包裝的理念是因為,方便工程師在開發期時 debug ,確認自己的 sql 有正確執行、也方便上線後監控每句 sql 的執行速度。